package com.my.simple.service.auth;

import com.my.simple.common.constant.CacheConstant;
import com.my.simple.common.enums.AdminRoleTypeEnum;
import com.my.simple.common.exception.UpdateRoleOperationException;
import com.my.simple.common.util.EHCacheUtils;
import com.my.simple.common.util.TokenUtil;
import com.my.simple.entity.auth.*;
import com.my.simple.service.bo.UserRoleBO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class AuthService {
    private static final Logger LOGGER = LoggerFactory.getLogger(AuthService.class);
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserService userService;
    @Autowired
    private OperationService operationService;


    public void updateRoleOperation(List<RoleOperation> roleOperationList) {
        LOGGER.debug("update role operation,{}", roleOperationList);

        if (roleOperationList != null && !roleOperationList.isEmpty()) {
            List<RoleOperation> existRoleOperationList = new ArrayList<>();
            List<Integer> roleIdList = roleOperationList.stream().map(p -> p.getRoleId()).distinct().collect(Collectors.toList());

            //get exist role operation
            if (roleIdList != null && !roleIdList.isEmpty()) {
                for (Integer roleId : roleIdList) {
                    List<RoleOperation> findRoleOperationListTemp = roleService.getRoleOperationByRoleId(roleId);
                    if (findRoleOperationListTemp != null && !findRoleOperationListTemp.isEmpty()) {
                        existRoleOperationList.addAll(findRoleOperationListTemp);
                    }
                }
            }

            //get need remove role operation
            List<RoleOperation> needRemoveRoleOperationList = getNeedRemoveList(existRoleOperationList, roleOperationList);
            //get need add new role operation
            List<RoleOperation> needAddRoleOperationList = getNeedAddList(existRoleOperationList, roleOperationList);

            //add transaction update by code logic replace by database lock
            changeRoleOperation(needRemoveRoleOperationList, needAddRoleOperationList);
        }
    }

    public void updateUserRole(List<UserRole> userRoleEntityList) {
        LOGGER.debug("update role operation,{}", userRoleEntityList);

        if (userRoleEntityList != null && !userRoleEntityList.isEmpty()) {
            List<UserRole> existUserRoleList = new ArrayList<>();
            List<Integer> userIdList = userRoleEntityList.stream().map(p -> p.getUserId()).distinct().collect(Collectors.toList());

            //get exist role operation
            if (userIdList != null && !userIdList.isEmpty()) {
                for (Integer userId : userIdList) {
                    List<UserRole> findUserRoleListTemp = userService.getUserRoleByUserId(userId);
                    if (findUserRoleListTemp != null && !findUserRoleListTemp.isEmpty()) {
                        existUserRoleList.addAll(findUserRoleListTemp);
                    }
                }
            }

            //get need remove role operation
            List<UserRole> needRemoveUserRoleList = getNeedRemoveList(existUserRoleList, userRoleEntityList);
            //get need add new role operation
            List<UserRole> needAddUserRoleList = getNeedAddList(existUserRoleList, userRoleEntityList);

            //add transaction update by code logic replace by database lock
            changeUserRole(needRemoveUserRoleList, needAddUserRoleList);
        }
    }

    private <T> List<T> getNeedRemoveList(List<T> existList, List<T> newList) {
        List<T> needRemoveList = new ArrayList<>();

        if (existList == null || existList.isEmpty()) {
            return needRemoveList;
        } else {

            if (newList == null || newList.isEmpty()) {
                return existList;

            } else {
                for (T item : existList) {
                    Boolean isInNewRoleOperation = newList.contains(item);
                    if (!isInNewRoleOperation) {
                        needRemoveList.add(item);
                    }
                }
            }
        }

        return needRemoveList;
    }

    private <T> List<T> getNeedAddList(List<T> existList, List<T> newList) {
        List<T> needAddRoleList = new ArrayList<>();

        if (newList == null || newList.isEmpty()) {
            return needAddRoleList;

        } else {
            if (existList == null || existList.isEmpty()) {
                return newList;
            }

            for (T item : newList) {
                Boolean isInExistRoleOperation = existList.contains(item);
                if (!isInExistRoleOperation) {
                    needAddRoleList.add(item);
                }
            }
        }

        return needAddRoleList;
    }

    public Boolean changeRoleOperation(List<RoleOperation> needRemoveRoleOperationList, List<RoleOperation> needAddRoleOperationList) {
        Boolean isChangeSuccess = true;

        try {
            if (needRemoveRoleOperationList != null && !needRemoveRoleOperationList.isEmpty()) {
                for (RoleOperation item : needRemoveRoleOperationList) {
                    roleService.deleteRoleOperation(item.getRoleId(), item.getOperationId());
                }
            }

            if (needAddRoleOperationList != null && !needAddRoleOperationList.isEmpty()) {
                for (RoleOperation item : needAddRoleOperationList) {
                    roleService.addRoleOperation(item.getRoleId(), item.getOperationId());
                }
            }

        } catch (Exception ex) {
            isChangeSuccess = false;
            LOGGER.error("change role operation error", ex);
            throw new UpdateRoleOperationException();
        }

        return isChangeSuccess;
    }

    public Boolean changeUserRole(List<UserRole> needRemoveUserRoleList, List<UserRole> needAddUserRoleList) {
        Boolean isChangeSuccess = true;

        try {
            if (needRemoveUserRoleList != null && !needRemoveUserRoleList.isEmpty()) {
                for (UserRole item : needRemoveUserRoleList) {
                    userService.deleteUserRole(item.getUserId(), item.getRoleId());
                }
            }

            if (needAddUserRoleList != null && !needAddUserRoleList.isEmpty()) {
                for (UserRole item : needAddUserRoleList) {
                    userService.addUserRole(item.getUserId(), item.getRoleId());
                }
            }

        } catch (Exception ex) {
            isChangeSuccess = false;
            LOGGER.error("change user role error", ex);
            throw new UpdateRoleOperationException();
        }

        return isChangeSuccess;
    }

    public String signToken(Integer userId) {
        String token = TokenUtil.generateToken(CacheConstant.CACHE_LOGIN_TOKEN);
        UserRoleBO userRoleInfo = getUserRoleInfoByUserId(userId);
        addToken(token, userRoleInfo);
        return token;
    }

    public void addToken(String token, UserRoleBO userRoleInfo) {
        EHCacheUtils.put(token, userRoleInfo);
    }

    public UserRoleBO getUserTokenMap(String token) {
        UserRoleBO userRoleBO = null;
        Object userInfo = EHCacheUtils.get(token);
        if (userInfo != null) {
            userRoleBO = (UserRoleBO) userInfo;
        }
        return userRoleBO;
    }

    public UserRoleBO getUserRoleInfoByUserId(Integer userId) {
        UserRoleBO userRoleBO = new UserRoleBO();
        List<Integer> userRoleIdList = new ArrayList<>();
        Boolean isAdmin = false;
        User user = userService.getUserById(userId);
        List<UserRole> userRoleEntityList = userService.getUserRoleByUserId(userId);

        if (userRoleEntityList != null && !userRoleEntityList.isEmpty()) {
            userRoleIdList = userRoleEntityList.stream().map(p -> p.getRoleId()).collect(Collectors.toList());
        }

        if (userRoleIdList != null && !userRoleIdList.isEmpty()) {
            for (Integer j : userRoleIdList) {
                if (AdminRoleTypeEnum.SuperAdmin.getKey().equals(j)) {
                    isAdmin = true;
                    break;
                }
            }
        }

        List<Operation> operationEntityList = operationService.getOperationByRoleIdList(userRoleIdList);

        userRoleBO.setUserInfo(user);
        userRoleBO.setUserRoleIds(userRoleIdList);
        userRoleBO.setIsAdmin(isAdmin);
        userRoleBO.setUserOperation(operationEntityList);
        return userRoleBO;
    }

}
